package com.atguigu.tingshu.payment.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.payment.PaymentInfo;
import com.atguigu.tingshu.payment.config.WxPayV3Config;
import com.atguigu.tingshu.payment.mapper.PaymentInfoMapper;
import com.atguigu.tingshu.payment.service.PaymentInfoService;
import com.atguigu.tingshu.payment.service.WxPayService;
import com.atguigu.tingshu.payment.util.PayUtil;
import com.atguigu.tingshu.user.client.UserInfoFeignClient;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;


import com.wechat.pay.java.core.exception.ServiceException;
import com.wechat.pay.java.core.exception.ValidationException;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;

import com.wechat.pay.java.service.payments.jsapi.model.*;
import com.wechat.pay.java.service.payments.model.Transaction;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class WxPayServiceImpl implements WxPayService {

    @Autowired
    private PaymentInfoService paymentInfoService;

    @Autowired
    private WxPayV3Config wxPayV3Config;

    @Autowired
    private UserInfoFeignClient userInfoFeignClient;

    @Autowired
    private PaymentInfoMapper paymentInfoMapper;

    @Autowired
    private RabbitService rabbitService;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    public Map<String, Object> createJsapi(String wxPayItemType, String orderNo) {

        //0.payment_info表中插入订单支付流水
        PaymentInfo paymentInfo = paymentInfoService.savePaymentInfo(wxPayItemType, orderNo);

        //1.构建config对象
        RSAAutoCertificateConfig config = wxPayV3Config.getConfig();

        //2.构建service对象 再利用config对象
        JsapiServiceExtension service = new JsapiServiceExtension.Builder().config(config).build();

        //3.调用预支付下单接口以及获取微信支付的必要参数
        // 跟之前下单实例一样，填充与下单参数
        PrepayRequest request = new PrepayRequest();


        //4.构建请求参数

        Amount amount = new Amount();
        //todo 生产环境 言根据订单编号取订单数据库中查询这个单的实际金额
        amount.setTotal(1); //言根据订单编号取订单数据库中查询这个单的实际金额 (测试数据 1)
        request.setAmount(amount);  //购买商品的总金额
        request.setAppid(wxPayV3Config.getAppid()); //appid
        request.setMchid(wxPayV3Config.getMerchantId());  //商户id
        request.setDescription("测试商品标题"); // 商品内容 在生成阶段，要根据订单表还去订单数据库中查询这个单的名字(标题)
        request.setNotifyUrl(wxPayV3Config.getNotifyUrl()); //异步回调地址
        request.setOutTradeNo(orderNo); //给微信平台的订单编号

        Payer payer = new Payer(); //构建付款者对象
        Long userId = AuthContextHolder.getUserId();
        Result<UserInfoVo> userInfo = userInfoFeignClient.getUserInfo(userId);
        UserInfoVo userInfoData = userInfo.getData();
        Assert.notNull(userInfoData, "用户信息不存在");

        payer.setOpenid(userInfoData.getWxOpenId()); //付款者的openid
        request.setPayer(payer);

        //5.发起请求 或者微信支付的必要参数
        // response包含了调起支付所需的所有参数，可直接用于前端调起支付
        PrepayWithRequestPaymentResponse response = service.prepayWithRequestPayment(request);

        //6.解析出来微信支付的必要参数
        String timeStamp = response.getTimeStamp();
        String nonceStr = response.getNonceStr();
        String packageVal = response.getPackageVal();
        String signType = response.getSignType();
        String paySign = response.getPaySign();

        //7.返回给前端
        Map<String, Object> map = new HashMap<>();
        map.put("timeStamp", timeStamp);
        map.put("nonceStr", nonceStr);
        map.put("package", packageVal);
        map.put("signType", signType);
        map.put("paySign", paySign);

        return map;
    }

    @Override
    public Transaction queryPayStatus(String orderNo, Long userId) {
        try {
            //1.构建一个请求对象
            QueryOrderByOutTradeNoRequest queryRequest = new QueryOrderByOutTradeNoRequest();
            queryRequest.setMchid(wxPayV3Config.getMerchantId());
            queryRequest.setOutTradeNo(orderNo);

            //2.构建service 再利用config对象
            JsapiServiceExtension service = new JsapiServiceExtension.Builder().config(wxPayV3Config.getConfig()).build();

            //3.查询该笔订单支付的结果
            Transaction transaction = service.queryOrderByOutTradeNo(queryRequest);
            //4.返回transaction对象
            return transaction;
        } catch (ServiceException e) {
            log.error("查询订单支付状态失败：{}", e.getErrorMessage());
            return null;
        }
    }

    @Override
    public void paySuccess(String orderNo, Transaction transaction) {
        //1.将payment_info表中的支付状态修改为已支付
        // 下商品单支付成功
        //2.将order_info中的订单状态修改为已支付
        //3.向用户支付流水表中插入记录(user_paid_track 或者user_paid_album 或者 user_vip_service)
        //4.修改MySQL中专辑的购买量
        //5.修改ES中的专辑购买量
        //充钱成功
        //2.将recharge_info中的订单状态修改为已支付
        //3 4 5 不用做了

        //1.将payment_info表中的支付状态修改为已支付
        int count = paymentInfoMapper.updatePaymentInfoStatus(orderNo);

        if (count > 0) {
            log.info("修改订单支付状态成功");
        } else {
            log.error("修改订单的支付状态失败 ");
        }

        //2.查询该笔订单的支付类型
        PaymentInfo paymentInfo = paymentInfoMapper.selectOne(
                new LambdaQueryWrapper<PaymentInfo>()
                        .eq(PaymentInfo::getOrderNo, orderNo));
        String paymentType = paymentInfo.getPaymentType();
//        if ("1301".equals(paymentType)){
//            //2.将order_info中的订单状态修改为已支付
//            //3.向用户支付流水表中插入记录(user_paid_track 或者user_paid_album 或者 user_vip_service)
//            //4.修改MySQL中专辑的购买量
//            //5.修改ES中的专辑购买量
//            rabbitService.sendMessage(MqConst.EXCHANGE_ORDER,MqConst.ROUTING_ORDER_PAY_SUCCESS,orderNo);
//        }else {
//            //2.将recharge_info中的订单状态修改为已支付
//            //3 4 5 不用做了
//            rabbitService.sendMessage(MqConst.EXCHANGE_ORDER,MqConst.ROUTING_RECHARGE_PAY_SUCCESS,orderNo);
//        }
        String routingKey = "1301".equals(paymentType) ? MqConst.ROUTING_ORDER_PAY_SUCCESS : MqConst.ROUTING_RECHARGE_PAY_SUCCESS;

        Map<String, String> msgMap = new HashMap<>();
        msgMap.put("orderNo", orderNo);
        String openId = transaction.getPayer().getOpenid();
        Result<String> userIdResult = userInfoFeignClient.getUserIdByOpenId(openId);
        String userId = userIdResult.getData();
        msgMap.put("userId", userId);

        String repeatQueryStatusKey = "order:pay:query:lock" + orderNo;
        Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent(repeatQueryStatusKey, "1",1, TimeUnit.MINUTES);
        if (aBoolean){
            rabbitService.sendMessage(MqConst.EXCHANGE_ORDER, routingKey, JSONObject.toJSONString(msgMap));
        }

    }

    @Override
    public Transaction asyncNotify(HttpServletRequest httpServletRequest) {
        //1.从原始请求的请求头中获取时间
        String signature = httpServletRequest.getHeader("Wechatpay-Signature");//微信回调时的签名
        String serial = httpServletRequest.getHeader("Wechatpay-Serial");//微信平台整数的序列化
        String nonce = httpServletRequest.getHeader("Wechatpay-Nonce"); //随机数
        String timestamp = httpServletRequest.getHeader("Wechatpay-Timestamp"); //签名中的时间戳
        String signType = httpServletRequest.getHeader("Wechatpay-Signature-Type"); //签名类型：默认是RSA

        //1.验签 --- 验证数据是不是微信平台回调给我的
        //2.解析：[解析密文] 转成可用的明文

        // 2.初始化 NotificationParser
        NotificationParser parser = new NotificationParser(wxPayV3Config.getConfig());
        String originContent = PayUtil.readData(httpServletRequest);
        // 3.构建请求参数对象
        RequestParam requestParam = new RequestParam.Builder()
                .serialNumber(serial)
                .nonce(nonce)
                .signature(signature)
                .timestamp(timestamp)
                .body(originContent)
                .build();
        try {
            //4.解析（1验签 2.解密 3.转换成 回调需要用到的Transaction对象）
            Transaction transaction = parser.parse(requestParam, Transaction.class);
            return transaction;
        } catch (ValidationException e) {
            // 签名验证失败，返回 401 UNAUTHORIZED 状态码
            log.error("签名验证失败:{}", e.getMessage());
            return null;
        }

    }
}
